import { WEB_ERROR_CODE } from "../enum/web-and-server-code";
import { WxHelp } from "./wx-help";
import { UniHelp } from "./uni-help";
import { Storage } from "./storage";
import Store from "@/store";
import dayjs from "dayjs";

export namespace Tool {
	/**
	 * 小于10首位补0
	 * @param {String | Number} value 要处理的值
	 */
	export function padZeor(value: number | string): string {
		if (!['number', 'string'].includes(typeof value)) {
			throw new Error('Pdhelp -> padZeor，参数格式错误');
		};
		return Number(value) < 10 ? `0${value}` : `${value}`;
	}
	/**
	 * 根据长度切割字符串
	 * @param {string} string 要处理的字符串
	 * @param {number} len 长度
	 * @return {Array<string>} 
	 */
	export function string2arrByLen(string: string, len: number): Array<string> {
		if (string.length <= len) {
			return [string];
		};
		const result: string[] = [];
		for (let i = 0; i < string.length; i += len) {
			result.push(string.substring(i, i + len));
		};
		return result;
	}

	/**
	 * canvas画圆型图片
	 * @param {CanvasDrawCircleImageOps} ops 
	 */
	export type CanvasDrawCircleImageOps = {
		ctx: UniApp.CanvasContext,//canvas上下文
		imgPath: string,//图片路径
		x: number,//图片x
		y: number,//图片y
		r: number,//图片半径
		padding?: number,//圆的padding
		fillColor?: string,//填充颜色
	};
	export function canvasDrawCircleImage(ops: CanvasDrawCircleImageOps): void {
		if (!ops.ctx || !ops.imgPath || !ops.r) {
			throw new Error('canvasDrawCircleImage缺省必要参数');
		};
		ops.ctx.save();
		const d = ops.r * 2;
		const cx = (ops.x ?? 0) + ops.r;
		const cy = (ops.y ?? 0) + ops.r;
		ops.ctx.arc(cx, cy, ops.r + (ops.padding ?? 0), 0, 2 * Math.PI);
		// ops.ctx.setFillStyle('transparent');
		ops.ctx.setFillStyle('#fff');
		ops.ctx.fill();
		ops.ctx.clip();
		ops.ctx.drawImage(ops.imgPath, ops.x ?? 0, ops.y ?? 0, d, d);
		ops.ctx.restore();
	}

	/**
	 * 延迟执行某个方法
	 * @param {Function} fn 
	 * @param {number} delay  延迟时间，单位毫秒
	 * @return 
	 */
	export function delayFnCall(fn: () => void, delay: number): void {
		if (!fn || typeof fn !== 'function') {
			throw new Error('delayFnCall参数格式错误');
		};
		setTimeout(() => {
			fn();
		}, delay);
	}
	/**
	 * 字符串转数组
	 * @param {stirng} string 需要处理的字符串
	 * @param {string} flag 分割符号
	 * @returns {string[]} 处理结果
	 */
	export const str2arr = (string: string, flag = '\n'): string[] => {
		if (typeof string !== 'string') {
			throw new Error('tools.ts->str2arr：参数错误');
		};
		return (!string || !string.length) ? [] : string.split(flag);
	};

	/**
	 * 对象转成路由参数
	 * @param {object | string} 需要处理的对象或字符串
	 * @returns {string} 如果传入字符串，则不做任何处理，直接返回， 如果是对象，则转换成xx=yy&zz=tt格式字符串
	 */
	export const paramsObj2string = (paramsObject: object | string): string => {
		if (!paramsObject) return '';
		if (!['string', 'object'].includes(typeof paramsObject)) {
			throw new Error('tool.ts->paramsObj2string参数格式错误');
		};
		if (typeof paramsObject === 'string') return paramsObject;
		if (!Object.keys(paramsObject).length) return '';
		return Object.keys(paramsObject).reduce((cur, next, index, arr) => {
			cur += `${next}=${paramsObject[next as keyof typeof paramsObject]}${index === arr.length - 1 ? '' : '&'}`;
			return cur;
		}, '');
	};

	/**
	 * 参数字符串转对象
	 * @param {string} paramsStr 参数字符串
	 * @returns {}
	 */
	/**
	 * 小程序scece转对象
	 * @param {string} paramString 
	 */
	export function paramString2Obj<T>(paramString: string): T {
		if (!paramString || !paramString.length) {
			return {} as T;
		};
		const obj = {};
		const paramsArr = paramString.split('&');
		for (let i = 0; i < paramsArr.length; i++) {
			const arr = paramsArr[i].split('=');
			obj[arr[0]] = arr[1];
		};
		return obj as T;
	}

	/**
	 * 版本号对比
	 * versionCompare(1.1.1, 1.1.0)  =>返回1
	 * versionCompare(1.1.1, 1.1.1)  =>返回0
	 * versionCompare(1.0.1, 1.1.1)  =>返回-1
	 */
	export function versionCompare(v1: string, v2: string): number {
		const version1 = v1.split('.');
		const version2 = v2.split('.');
		const len = Math.max(v1.length, v2.length);
		while (version1.length < len) {
			version1.push('0');
		};
		while (version2.length < len) {
			version2.push('0');
		};
		for (let i = 0; i < len; i++) {
			const num1 = parseInt(version1[i]);
			const num2 = parseInt(version2[i]);
			if (num1 > num2) {
				return 1;
			} else if (num1 < num2) {
				return -1;
			};
		};
		return 0;
	};
	/**
	 * 计算两个时间戳时间相差多远
	 * @oaram {number} start 开始时间戳*11位
	 * @param {number} end 结束时间戳*11位
	 * @returns <CalcTimestampDurationRes.Day> 两个时间相差天数
	 * @returns <CalcTimestampDurationRes.hour> 两个时间相差小时数（扣除天数后）
	 * @returns <CalcTimestampDurationRes.minutes> 两个时间相差分钟数(扣除天+小时后)
	 * @returns <CalcTimestampDurationRes.second> 两个时间相差秒数（扣除天+小时+分钟）
	 * @returns <CalcTimestampDurationRes.timestamp> 
	 */
	type CalcTimestampDurationRes = {
		day: number,
		hour: number,
		minutes: number,
		second: number,
		timestamp: number
	}
	export function calcTimestampDuration(start: number, end: number): CalcTimestampDurationRes {
		const diff = end - start;
		const day = Math.floor(diff / (24 * 60 * 60 * 1000));
		const hour = Math.floor(diff / (60 * 60 * 1000)) % 24;
		const minutes = Math.floor(diff / (60 * 1000)) % 60;
		const second = Math.floor(diff / 1000) % 60;
		return {
			day,
			hour,
			minutes,
			second,
			timestamp: diff
		};
	}

	/**
	 * try catch 统一异常抛出
	 */
	export function tryCatchErrorHandler(
		err: unknown,
		promiseRejecter: (p: { code: WEB_ERROR_CODE, msg: unknown }) => void
	) {
		WxHelp.WxLogger.codeError(err);
		console.error(err);
		return promiseRejecter({
			code: WEB_ERROR_CODE.unkonw,
			msg: err
		});
	}

	/**
	 * 根据start/end生成数组
	 * @param {number} start 起始值 
	 * @param {number} end 终止值
	 */
	export function createArray(start: number, end: number): number[] {
		const v: number[] = [];
		for (let i = start; i <= end; i++) {
			v.push(i);
		};
		return v;
	}

	/**
	 * 获取某个日期的当天的最晚结束时间戳
	 * @param {number} timestamp 要处理的日期的时间差
	 * @returns {number} 处理后的时间戳，单位毫秒
	 * demo : 2022-08-23 => 2022-08-23 23:59:59 的时间戳
	 */
	export function getDayEndTimestamp(timestamp: number): number {
		const v = new Date(timestamp);
		return new Date(v.getFullYear(), v.getMonth(), v.getDate(), 23, 59, 59).getTime();
	}

	/**
	 * 根据文件名称获取文件的后缀名
	 * @param {string} fileName 文件名 
	 * @returns {string} 文件后缀名
	 */
	export function getExtByFileName (fileName : string) {
		if (!fileName) throw new Error('getFileExt缺乏必要参数');
		if (typeof fileName !== 'string') throw new Error('getExtByFileName参数类型错误');
		const match = fileName.match(/\.([^.]*)$/);
		if (match && match[1]) {
			return match[1];
		};
		return '';
	}

	/**
	 * 从httpurl中获取文件名部分
	 * @param {string} httpUrl demo : http://abc.png
	 * @returns {string} demo : abc.png
	 */
	export function getFileNameFromHttpUrl (httpUrl : string) : string {
		const regex = /\.com\/(.*)/;
		const match = httpUrl.match(regex);
		return match ? match[1] : '';
	}

	/**
	 * 生成随机数
	 * @param min 随机数起始
	 * @param max 随机数终止
	 * @returns 
	 */
	export function getRandomInt (min : number, max : number) : number {
		return Math.floor(Math.random() * (max - min + 1)) + min;
	}

	/**
	 * canvas画文字
	 */
	type FillTextOps = {
		text : string,
		color ?: string,
		textAlign ?: 'left' | 'center' | 'right',
		fontSize : number,
		boldText ?: boolean,
		x : number,
		y : number,
		maxWidth ?: number,
		lineHeight ?: number
	}
	export function canvasFillText (ctx : UniApp.CanvasContext, ops : FillTextOps, transitionUnit = true) : {metrics : UniApp.CanvasTextMetrics, x : number, y :number} {
		ctx.beginPath();
		const fontSize = transitionUnit ? uni.upx2px(ops.fontSize) : ops.fontSize;
		const x = transitionUnit ? uni.upx2px(ops.x) : ops.x;
		const y = transitionUnit ? uni.upx2px(ops.y) : ops.y;
		ops.fontSize && ctx.setFontSize(transitionUnit ? uni.upx2px(ops.fontSize) : ops.fontSize);
		ops.color && ctx.setFillStyle(ops.color);
		ops.textAlign && ctx.setTextAlign(ops.textAlign);
		ops.boldText ? (ctx.font = `normal bold ${fontSize}px serif`) : (ctx.font = `normal ${fontSize}px serif`);
		ctx.fillText(ops.text,x,y);
		ctx.stroke();
		ctx.closePath();
		return {
			metrics : ctx.measureText(ops.text),
			x,
			y,
		};
	}
	export function getTextWidthInCanvas (ctx : UniApp.CanvasContext, ops : FillTextOps, transitionUnit = true) : number {
		const fontSize = transitionUnit ? uni.upx2px(ops.fontSize) : ops.fontSize;
		ctx.font = ops.boldText ? `normal bold ${fontSize}px serif` : `normal ${fontSize}px serif`;
		return ctx.measureText(ops.text).width;
	}

	/**
	 * canvas画文字-自动换行
	 */
	export function canvasFillTextWithWrap (ctx : UniApp.CanvasContext, ops : FillTextOps, transitionUnit = true) {
		const words = ops.text.split('');
		const maxWidth = transitionUnit ? uni.upx2px(ops.maxWidth || 0) : (ops.maxWidth || 0);
		const lineHeight = transitionUnit ? uni.upx2px(ops.lineHeight || 0) : (ops.lineHeight || 0);
		let startY = ops.y;
		let line = '';
		for (let i = 0; i < words.length; i ++) {
			const testLine = line + words[i];
			const metrics = getTextWidthInCanvas(ctx, {
				text : testLine,
				fontSize : ops.fontSize,
				boldText : ops.boldText,
				color : ops.color,
				x :0,
				y :0
			});
			if (metrics > maxWidth && i > 0) {//超出了就画一行
				canvasFillText(ctx, {
					...ops,
					text : line,
					x : ops.x,
					y : startY
				});
				line = words[i];
				startY += lineHeight;
			} else {
				line = testLine;
			};
		};
		//画剩余的一行
		canvasFillText(ctx, {
			...ops,
			text : line,
			x : ops.x,
			y : startY
		});
		return startY;
	}

	/**
	 * canvas画圆
	 */
	type DrawArcOps = {
		x : number,
		y : number,
		radius : number,
		bgColor : string,
	}
	export function cavasDrawArc (ctx : UniApp.CanvasContext , ops : DrawArcOps, transitionUnit = true) {
		ctx.beginPath();
		const x = transitionUnit ? uni.upx2px(ops.x) : ops.x;
		const y = transitionUnit ? uni.upx2px(ops.y) : ops.y;
		const r = transitionUnit ? uni.upx2px(ops.radius) : ops.radius;
		ctx.arc(x, y, r, 0, 2*Math.PI);
		ops.bgColor && ctx.setFillStyle(ops.bgColor);
		ctx.fill();
		ctx.closePath();
	}
	
	/**
	 * 绘制图片
	 * @returns {void}
	 * @param {string} ctx 实例
	 * @param {string} url 图片网络地址、临时地址
	 * @param {number} x x轴距离
	 * @param {number} y y轴距离
	 * @param {number} w 图片宽
	 * @param {number} h 图片高
	 */
	export function drawImgBg (ctx : UniApp.CanvasContext,url : string,x : number,y : number,w : number,h : number) : Promise<void> {
		return new Promise(async(r)=>{
			ctx.save();
			ctx.beginPath();
			const imgUrl = await UniHelp.downloadFile(url);
			const imgW = uni.upx2px(w); //尺寸宽
			const imgH = uni.upx2px(h); //尺寸高
			const imgX = uni.upx2px(x); //x轴
			const imgY = uni.upx2px(y); //y轴
			ctx.drawImage(imgUrl,imgX,imgY,imgW,imgH);
			ctx.closePath();
			ctx.restore();
			r();
		});
	}
	
	/**
	 * canvas绘制带圆角的长方形或正方形
	 * @param {int} w 宽
	 * @param {int} h 高
	 * @param {int} x x轴距离 - int
	 * @param {int} y y轴距离 - int
	 * @param {int} radius 半径 - int
	 * @param {string} col 填充颜色
	 * @param {boolean} isGradient 是否要设置渐变色
	 */
	export function shapeBorderRadius (ctx : UniApp.CanvasContext,w : number,h : number,x : number,y : number,radius : number,col : string, isGradient = false) : Promise<void> {
		return new Promise(async(r) => {
			const clipW = uni.upx2px(w); //宽尺寸
			const clipH = uni.upx2px(h); //高尺寸
			const clipX = uni.upx2px(x);
			const clipY = uni.upx2px(y);
			const clipR = uni.upx2px(radius); //圆角
			const cardBg = ctx.createLinearGradient(clipX,clipY,clipX,clipY + clipH);
			//设置渐变色
			if(isGradient){
				cardBg.addColorStop(0, '#20BFFF');
				cardBg.addColorStop(1, '#0972F1');
			};
			//绘制上直线与右上角圆角
			ctx.moveTo(clipX + clipR, clipY);
			ctx.arcTo(clipX + clipW, clipY, clipX + clipW, clipY + clipR, clipR);
			// 右边直线
			ctx.lineTo(clipX + clipW, clipY + clipH - clipR);
			// 右下角圆角
			ctx.arcTo(clipX + clipW , clipY + clipH, clipX + clipW - clipR, clipY + clipH, clipR);
			// 下边直线
			ctx.lineTo(clipX + clipR, clipY + clipH);
			//左下角圆角
			ctx.arcTo(clipX,clipY + clipH,clipX,clipY + clipH - clipR,clipR);
			//左边直线
			ctx.lineTo(clipX, clipY + clipR);
			// 左上角圆角
			ctx.arcTo(clipX,clipY,clipX + clipR,clipY,clipR);
			ctx.setFillStyle(col || cardBg);
			ctx.fill();
			r();
		});
	}
	
	/**
	 * 本地获取缓存，距离上一次是否有超过1天
	 * @returns {void} 配置信息接口返回的数据缓存在本地
	 * @param {string} key 缓存的key
	 */
	type DayKeyItem = 'transferOrderRemark' | 'resetDayOpen' | 'sellGuidanceTime' | 'quickTextList';
	export function getStorageByDay <T>(key : DayKeyItem) : Promise<T> {
		return new Promise((r,j)=>{
			const keyObj = Storage.get(key);
			const driver_id = Store.driver().info.id;//司机id
			//在本地有缓存，并且司机id对应
			if(keyObj && keyObj.day && keyObj.driver_id === driver_id){
				const nowDate = dayjs();
				const nextDate = dayjs(keyObj.day);
				const diffDate = nowDate.diff(nextDate,'day');
				if(diffDate === 0){//是当天
					r(keyObj.list);
					return;
				};
				j();
				return;
			};
			j();
		});
	}
	
	/**
	 * 本地缓存，时间为当前时间
	 * @returns {void}
	 * @param {string} key 缓存的key
	 * @param {string} val 缓存的数据,配置信息接口返回的数据缓存在本地
	 */
	// eslint-disable-next-line @typescript-eslint/no-explicit-any
	export function setStorageByDay (key : DayKeyItem, val : any) {
		const obj = {
			day : dayjs().format('YYYY-MM-DD'),
			list : val,
			driver_id : Store.driver().info.id
		};
		UniHelp.setStorage(key,obj);
	}
	
	/**
	 * 时间与当前时间相差时间 key代表Day.js的差异单位
	 * @returns {void}
	 * @param {string} key 
	 * @param {number} val 对比的时间
	 */
	export function timeDiff(val : number, key : 'day' | 'hour', flag = 1000) {
		if(val === 0){
			return 6;
		};
		const v = dayjs(val * flag).format('YYYY-MM-DD HH:mm:ss');
		return dayjs().diff(v,key);
	}
}